#include "KeyRecommander.hh"
#include "nlohmann/json.hpp"
#include "Cache/SingnalRedis.hh"
#include "Configuration.hh"
#include "Log/Log.hh"
#include "Cache/CacheManager.hh"

#include <thread>

using std::this_thread::get_id;


using std::set;
using std::string;

using namespace nlohmann;

int editDistance(const std::string & lhs, const std::string &rhs);
std::size_t length(const std::string &str);

KeyRecommander::KeyRecommander(const string& query,  TcpConnectionPtr conn)
    : _queryWord(query), _conn(conn){

}

// 顺手加入优先级队列
void KeyRecommander::execute(){
    auto& dict = Dictionary::createInstance()->getDict();

    const auto& ret = Dictionary::createInstance()->doQuery(_queryWord);
	Log::debug(_queryWord);
	Log::debug("query: %d", ret.size());

    for(auto& elem : ret){
		if(length(_queryWord) != _queryWord.size()){
			//中文推荐
			// Log::debug("中文推荐");
			if(length(dict[elem].first) == dict[elem].first.size()){
				// 找到的是英文
				
			}else{
				// 找到的是中文
				Log::debug("%d", elem);
				Log::debug(dict[elem].first);
				_resultQueue.push({distanse(dict[elem].first), dict[elem].second, dict[elem].first});
			}
		}else{
			//英文推荐
			if(length(dict[elem].first) == dict[elem].first.size()){
				// 找到的是英文
				Log::debug(dict[elem].first);
				_resultQueue.push({distanse(dict[elem].first), dict[elem].second, dict[elem].first});
			}else{
				// 找到的是中文
				
			}
		}
    }
	Log::debug("queue size: %d", _resultQueue.size());

}

void KeyRecommander::response(){
	// 根据优先级队列取前10个
	nlohmann::json json_array = nlohmann::json::array();
	int count = _resultQueue.size() > 10 ? 10 : _resultQueue.size();
	for(int i = 0; i < count; ++i){
		json_array.push_back(_resultQueue.top().word);
		Log::debug(_resultQueue.top().word);
		_resultQueue.pop();
	}

	string json = json_array.dump();

	// 查询出的结果放入Cache
	thread::id p_id = get_id();

    auto pid_vec = CacheManager::createCacheManager()->getPidVec();
    size_t serial = 0;
    for(; serial < pid_vec.size(); ++serial){
        if(pid_vec[serial] == p_id){
            break;
        }
    }

	CacheManager::createCacheManager()->getCaches()[serial].addElement(string("100") + _queryWord, json);
	_conn->sendInLoop(json);
    Log::info("%ld 号Cache存储成功, Key: %s", serial, string("200") + _queryWord.c_str());

	// // 查询出的结果放入redis
    // if(SingnalRedis::createRedis()->getRedis().set(string("100") + _queryWord, json) == false){
    //     cout << "redis set error" << "\n";
    // }
    // cout << "redis set success" << "\n";
	// _conn->sendInLoop(json);
}


int KeyRecommander::distanse(const string &rhs){
    return editDistance(_queryWord, rhs);
}


inline size_t nBytesCode(const char ch)
{
	if(ch & (1 << 7))
	{
		int nBytes = 1;
		for(int idx = 0; idx != 6; ++idx)
		{
			if(ch & (1 << (6 - idx)))
			{
				++nBytes;	
			}
			else
				break;
		}
		return nBytes;
	}
	return 1;
}  
  
inline std::size_t length(const std::string &str)
{
	std::size_t ilen = 0;
	for(std::size_t idx = 0; idx != str.size(); ++idx)
	{
		int nBytes = nBytesCode(str[idx]);
		idx += (nBytes - 1);
		++ilen;
	}
	return ilen;
}

inline int triple_min(const int &a, const int &b, const int &c)
{
	return a < b ? (a < c ? a : c) : (b < c ? b : c);
}

inline int editDistance(const std::string & lhs, const std::string &rhs)
{//计算最小编辑距离-包括处理中英文
	size_t lhs_len = length(lhs);
	size_t rhs_len = length(rhs);
	int editDist[lhs_len + 1][rhs_len + 1];
	for(size_t idx = 0; idx <= lhs_len; ++idx)
	{
		editDist[idx][0] = idx;
	}

	for(size_t idx = 0; idx <= rhs_len; ++idx)
	{
		editDist[0][idx] = idx;
	}
	
	std::string sublhs, subrhs;
	for(std::size_t dist_i = 1, lhs_idx = 0; dist_i <= lhs_len; ++dist_i, ++lhs_idx)
	{
		size_t nBytes = nBytesCode(lhs[lhs_idx]);
		sublhs = lhs.substr(lhs_idx, nBytes);
		lhs_idx += (nBytes - 1);

		for(std::size_t dist_j = 1, rhs_idx = 0; dist_j <= rhs_len; ++dist_j, ++rhs_idx)
		{
			nBytes = nBytesCode(rhs[rhs_idx]);
			subrhs = rhs.substr(rhs_idx, nBytes);
			rhs_idx += (nBytes - 1);
			if(sublhs == subrhs)
			{
				editDist[dist_i][dist_j] = editDist[dist_i - 1][dist_j - 1];
			}
			else
			{
				editDist[dist_i][dist_j] = triple_min(
					editDist[dist_i][dist_j - 1] + 1,
					editDist[dist_i - 1][dist_j] + 1,
					editDist[dist_i - 1][dist_j - 1] + 1);
			}
		}
	}
	return editDist[lhs_len][rhs_len];
}